import {sm2, sm3, sm4} from 'sm-crypto-v2';

interface KeyPair {
  privateKey: string;
  publicKey: string;
}

/**
 * sm3哈希算法
 * @param plainText 需要计算的明文
 */
export function sm3Hash(plainText: string) {
  return sm3(plainText);
}

/**
 * 生成sm4密钥
 * @param len 长度,默认位32个字符,128位
 */
export function sm4GenerateKey(len: number = 32) {
  return [...Array(len)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
}


/**
 * sm4加密,cbc模式,使用 pkcs#7 填充
 * @param plainText 明文
 * @param key 密钥,16进制字符串，要求为128位
 * @param iv 向量,16进制字符串，要求为128位
 */
export function sm4Encrypt(plainText: string, key: string, iv: string) {
  return sm4.encrypt(plainText, key, {
    mode: 'cbc',
    iv: iv,
    output: 'string',
  });
}

/**
 * sm4解密,cbc模式,使用 pkcs#7 填充
 * @param cipherText 密文,16进制串
 * @param key 密钥,16进制串，要求为128位
 * @param iv 向量,16进制字符串，要求为128位
 */
export function sm4Decrypt(cipherText: string, key: string, iv: string) {
  return sm4.decrypt(cipherText, key, {
    mode: 'cbc',
    iv: iv,
    output: 'string'
  });
}

/**
 * 生成sm2私钥和公钥对
 */
export function sm2GenerateKeyPairHex(): KeyPair {
  return sm2.generateKeyPairHex();
}

/**
 * 生成压缩公钥
 * @param publicKey 公钥,16进制字符串
 */
export function sm2CompressPublicKeyHex(publicKey: string): string {
  return sm2.compressPublicKeyHex(publicKey);
}

/**
 * 验证公钥是否为椭圆曲线上的点
 * @param publicKey 公钥,16进制字符串
 */
export function sm2VerifyPublicKey(publicKey: string): boolean {
  return sm2.verifyPublicKey(publicKey);
}

/**
 * 验证公钥是否等价，等价返回true
 * @param publicKey1 公钥1,16进制字符串
 * @param publicKey2 公钥2,16进制字符串
 */
export function sm2ComparePublicKeyHex(publicKey1: string, publicKey2: string): boolean {
  return sm2.comparePublicKeyHex(publicKey1, publicKey2);
}


/**
 * sm2加密
 * @param plainText 明文
 * @param publicKey 公钥,16进制字符串
 * @param cipherMode 加密模式: 1 - C1C3C2，0 - C1C2C3，默认为1
 */
export function sm2Encrypt(plainText: string, publicKey: string, cipherMode: number = 1): string {
  return sm2.doEncrypt(plainText, publicKey, cipherMode);
}

/**
 * sm2解密
 * @param cipherText 密文,16进制字符串
 * @param privateKey 私钥,16进制字符串
 * @param cipherMode 加密模式: 1 - C1C3C2，0 - C1C2C3，默认为1
 */
export function sm2Decrypt(cipherText: string, privateKey: string, cipherMode: number = 1): string {
  return sm2.doDecrypt(cipherText, privateKey, cipherMode, {
    output: 'string',
  });
}


/**
 * 签名
 * @param plainText 明文
 * @param privateKey 私钥,16进制字符串
 */
export function sm2Signature(plainText: string, privateKey: string): string {
  return sm2.doSignature(plainText, privateKey,{der:true,hash:true});
}

/**
 * 验签
 * @param plainText 明文
 * @param signHex 签名值,16进制字符串
 * @param publicKey 公钥,16进制字符串
 */
export function sm2VerifySignature(plainText: string, signHex: string, publicKey: string): boolean {
  return sm2.doVerifySignature(plainText, signHex, publicKey,{der:true,hash:true});
}


const u8a = (a: any): a is Uint8Array => a instanceof Uint8Array;
const hexes = Array.from({length: 256}, (v, i) =>
  i.toString(16).padStart(2, '0')
);

export function bytesToHex(bytes: Uint8Array): string {
  if (!u8a(bytes)) throw new Error('Uint8Array expected');
  // pre-caching improves the speed 6x
  let hex = '';
  for (let i = 0; i < bytes.length; i++) {
    hex += hexes[bytes[i]];
  }
  return hex;
}

export function hexToBytes(hex: string): Uint8Array {
  const len = hex.length;
  if (len % 2)
    throw new Error(
      'padded hex string expected, got unpadded hex of length ' + len
    );
  const array = new Uint8Array(len / 2);
  for (let i = 0; i < array.length; i++) {
    const j = i * 2;
    const hexByte = hex.slice(j, j + 2);
    const byte = Number.parseInt(hexByte, 16);
    if (Number.isNaN(byte) || byte < 0) throw new Error('Invalid byte sequence');
    array[i] = byte;
  }
  return array;
}

/**
 * uint8array数组转为base64字符串
 */
export function bytesToBase64(bytes: Uint8Array): string {
  let base64 = btoa(String.fromCharCode(...bytes));
  return base64
    .replace(/\+/g, '-')   // 替换 + 为 -
    .replace(/\//g, '_')   // 替换 / 为 _
    .replace(/=+$/, '');   // 删除末尾填充的=
}

/**
 * base64字符串转为uint8array数组
 */
export function base64ToBytes(base64: string) {
  // 还原为标准Base64格式
  let base64Res = base64
    .replace(/-/g, '+')   // 替换 - 为 +
    .replace(/_/g, '/');  // 替换 _ 为 /

  // 补充必要的=填充符
  const padLength = (4 - (base64Res.length % 4)) % 4;
  base64Res += '='.repeat(padLength);

  // 解码二进制数据
  const binary = atob(base64Res);
  const bytes = new Uint8Array(binary.length);

  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes;
}